package org.go.ee.servlet;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import org.go.domain.GoDomain;
import org.go.log.Log;
import org.go.log.LoggerFactory;
import org.go.scheduler.Scheduler;
import org.go.scheduler.StdSchedulerFactory;

/**
 * <p>
 * A ServletContextListner that can be used to initialize Quartz.
 * </p>
 *
 * <p>
 * You'll want to add something like this to your WEB-INF/web.xml file:
 *
 * <pre>
 *     &lt;context-param&gt;
 *         &lt;param-name&gt;quartz:config-file&lt;/param-name&gt;
 *         &lt;param-value&gt;/some/path/my_quartz.properties&lt;/param-value&gt;
 *     &lt;/context-param&gt;
 *     &lt;context-param&gt;
 *         &lt;param-name&gt;quartz:shutdown-on-unload&lt;/param-name&gt;
 *         &lt;param-value&gt;true&lt;/param-value&gt;
 *     &lt;/context-param&gt;
 *     &lt;context-param&gt;
 *         &lt;param-name&gt;quartz:wait-on-shutdown&lt;/param-name&gt;
 *         &lt;param-value&gt;true&lt;/param-value&gt;
 *     &lt;/context-param&gt;
 *     &lt;context-param&gt;
 *         &lt;param-name&gt;quartz:start-on-load&lt;/param-name&gt;
 *         &lt;param-value&gt;true&lt;/param-value&gt;
 *     &lt;/context-param&gt;
 *     
 *     &lt;listener&gt;
 *         &lt;listener-class&gt;
 *             org.quartz.ee.servlet.QuartzInitializerListener
 *         &lt;/listener-class&gt;
 *     &lt;/listener&gt;
 * </pre>
 *
 * </p>
 * <p>
 * The init parameter 'quartz:config-file' can be used to specify the path (and
 * filename) of your Quartz properties file. If you leave out this parameter,
 * the default ("quartz.properties") will be used.
 * </p>
 *
 * <p>
 * The init parameter 'quartz:shutdown-on-unload' can be used to specify whether you
 * want scheduler.shutdown() called when the listener is unloaded (usually when
 * the application server is being shutdown). Possible values are "true" or
 * "false". The default is "true".
 * </p>
 *
 * <p>
 * The init parameter 'quartz:wait-on-shutdown' has effect when 
 * 'quartz:shutdown-on-unload' is specified "true", and indicates whether you
 * want scheduler.shutdown(true) called when the listener is unloaded (usually when
 * the application server is being shutdown).  Passing "true" to the shutdown() call
 * causes the scheduler to wait for existing jobs to complete. Possible values are 
 * "true" or "false". The default is "false".
 * </p>
 * 
 * <p>
 * The init parameter 'quartz:start-on-load' can be used to specify whether
 * you want the scheduler.start() method called when the listener is first loaded.
 * If set to false, your application will need to call the start() method before
 * the scheduler begins to run and process jobs. Possible values are "true" or
 * "false". The default is "true", which means the scheduler is started.
 * </p>
 *
 * A StdSchedulerFactory instance is stored into the ServletContext. You can gain access
 * to the factory from a ServletContext instance like this:
 * <br>
 * <pre>
 * StdSchedulerFactory factory = (StdSchedulerFactory) ctx
 *                .getAttribute(QuartzInitializerListener.QUARTZ_FACTORY_KEY);</pre>
 * <p>
 * The init parameter 'quartz:servlet-context-factory-key' can be used to override the
 * name under which the StdSchedulerFactory is stored into the ServletContext, in 
 * which case you will want to use this name rather than 
 * <code>QuartzInitializerListener.QUARTZ_FACTORY_KEY</code> in the above example.
 * </p>
 *
 * <p>
 * The init parameter 'quartz:scheduler-context-servlet-context-key' if set, the 
 * ServletContext will be stored in the SchedulerContext under the given key
 * name (and will therefore be available to jobs during execution). 
 * </p>
 *
 * <p>
 * The init parameter 'quartz:start-delay-seconds' can be used to specify the amount
 * of time to wait after initializing the scheduler before scheduler.start()
 * is called.
 * </p>
 *
 * Once you have the factory instance, you can retrieve the Scheduler instance by calling
 * <code>getScheduler()</code> on the factory.
 *
 * @author hejie
 */
public class GoInitListener implements ServletContextListener {

	private static final Log log = LoggerFactory.getLog(GoInitListener.class);
	private boolean performShutdown = true;

	private Scheduler scheduler = null;

	@SuppressWarnings("unused")
	private boolean waitOnShutdown = false;

	@Override
	public void contextDestroyed(ServletContextEvent sce) {
		if (!performShutdown) {
			return;
		}
		try {
			if (scheduler != null) {
				scheduler.shutdown(true);
			}
		} catch (Exception e) {
			log.error("Go Scheduler failed to shutdown cleanly: " + e.toString());
			e.printStackTrace();
		}
		log.info("Go Scheduler successful shutdown.");
	}

	/*
	 * (non-Javadoc)
	 * @see javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent)
	 */
	@Override
	public void contextInitialized(ServletContextEvent sce) {

		log.info("Go Scheduler Initializer Servlet loaded, initializing Scheduler...");

		ServletContext servletContext = sce.getServletContext();
		StdSchedulerFactory factory;
		try {
			String configFile = servletContext.getInitParameter("go:config-file");
			if (configFile == null)
				configFile = servletContext.getInitParameter("config-file"); // older name, for backward compatibility
			String shutdownPref = servletContext.getInitParameter("go:shutdown-on-unload");
			if (shutdownPref == null)
				shutdownPref = servletContext.getInitParameter("shutdown-on-unload");
			if (shutdownPref != null) {
				performShutdown = Boolean.valueOf(shutdownPref).booleanValue();
			}
			String shutdownWaitPref = servletContext.getInitParameter("go:wait-on-shutdown");
			if (shutdownPref != null) {
				waitOnShutdown = Boolean.valueOf(shutdownWaitPref).booleanValue();
			}

			//get Properties
			if (configFile != null) {
				factory = new StdSchedulerFactory(configFile);
			} else {
				factory = new StdSchedulerFactory();
			}

			//Always want to get the scheduler, even if it isn't starting, 
			//to make sure it is both initialized and registered.
			//scheduler = factory.getScheduler();
			//TODO
			factory.getAllSchedulers();
			// Should the Scheduler being started now or later
			String startOnLoad = servletContext.getInitParameter("go:start-on-load");
			if (startOnLoad == null)
				startOnLoad = servletContext.getInitParameter("start-scheduler-on-load");

			int startDelay = 0;
			String startDelayS = servletContext.getInitParameter("go:start-delay-seconds");
			if (startDelayS == null)
				startDelayS = servletContext.getInitParameter("start-delay-seconds");
			try {
				if (startDelayS != null && startDelayS.trim().length() > 0)
					startDelay = Integer.parseInt(startDelayS);
			} catch (Exception e) {
				log.error("Cannot parse value of 'start-delay-seconds' to an integer: " + startDelayS + ", defaulting to 5 seconds.");
				startDelay = 5;
			}

			/*
			 * If the "quartz:start-on-load" init-parameter is not specified,
			 * the scheduler will be started. This is to maintain backwards
			 * compatability.
			 */
			if (startOnLoad == null || (Boolean.valueOf(startOnLoad).booleanValue())) {
				if (startDelay <= 0) {
					// Start now
					//scheduler.startAll();
					log.info("Scheduler has been started...");
				} else {
					// Start delayed
					//scheduler.startDelayed(startDelay);
					log.info("Scheduler will start in " + startDelay + " seconds.");
				}
			} else {
				log.info("Scheduler has not been started. Use scheduler.start()");
			}

			String factoryKey = servletContext.getInitParameter("quartz:servlet-context-factory-key");
			if (factoryKey == null)
				factoryKey = servletContext.getInitParameter("servlet-context-factory-key");
			if (factoryKey == null) {
				factoryKey = GoDomain.QUARTZ_FACTORY_KEY;
			}

			log.info("Storing the Go Scheduler Factory in the servlet context at key: " + factoryKey);
			servletContext.setAttribute(factoryKey, factory);

			String servletCtxtKey = servletContext.getInitParameter("go:scheduler-context-servlet-context-key");
			if (servletCtxtKey == null)
				servletCtxtKey = servletContext.getInitParameter("scheduler-context-servlet-context-key");
			if (servletCtxtKey != null) {
				log.info("Storing the ServletContext in the scheduler context at key: " + servletCtxtKey);
				scheduler.getContext().put(servletCtxtKey, servletContext);
			}
		} catch (Exception e) {
			log.error("Go Scheduler failed to initialize: " + e.toString());
		}
	}
}
